package extractors;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;

import parser.ArticleParser;
import primitive.Article;
import primitive.Pair;
import utils.ValueComparator;

public class OurExtractor implements Extractor {
	
	private List<Article> articles_list = new ArrayList<>();
	private List<Pair> labels_list = new ArrayList<>();
	private List<HashMap<String, List<Double>>> features_vectors = new ArrayList<>();
	
	private ArticleParser parser = new ArticleParser();
	
	private HashMap<String, HashMap<String, Integer>> most_popular_words = new HashMap<>();
	private HashMap<String, List<String>> terms_by_category = new HashMap<>();
	private List<String> forbidden = new ArrayList<>();
	private List<String> terms = new ArrayList<>();
	
	private int no_terms_by_category = 5;
	private int forb_per_cat_limit = 100;
	
	@Override
	public void set_labels(List<Pair> labels) {
		labels_list = labels;
	}

	@Override
	public void featuresExtraction(List<Article> aritlces) {
		articles_list = aritlces;
		
		create_popular_words_list();
		find_most_popular_words();
		create_forbidden_words();
		select_terms();
		
		// Extraction features vectors of training set
		for (int i = 0; i < articles_list.size(); ++i) {
			List<Double> vector = extraction(articles_list.get(i));
			String category = articles_list.get(i).getValue(labels_list.get(0).first);
			HashMap<String, List<Double>> record = new HashMap<>();
			record.put(category, vector);
			features_vectors.add(record);
		}
		
		System.out.println(terms);
		
		System.out.println("Vectors size = " + articles_list.size());
		
	}

	@Override
	public void featuresSave(String filename) {
		
		try {
			FileWriter fstream = new FileWriter(filename);
			BufferedWriter out = new BufferedWriter(fstream);
			
			String line = "";
			
			// Write key words
			for (int i = 0; i < terms.size(); ++i) {
				line += terms.get(i) + ";";
			}
			
			line = line.substring(0, line.length() - 1);
			out.write(line + "\n");
			out.flush();
			
			System.out.println(features_vectors.size());
			for (int i = 0; i < features_vectors.size(); ++i) {
				line = "";
				String key = features_vectors.get(i).entrySet().iterator().next().getKey();
				List<Double> vec = features_vectors.get(i).get(key);
				
				line += key + "|";
				for (int j = 0; j < vec.size(); ++j) {
					line += vec.get(j) + ";";
				}
				
				line = line.substring(0, line.length() - 1);
				out.write(line + "\n");
			}
			
			out.close();
			
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		
	}

	@Override
	public void loadFeatures(String filename) {
		
		try {
			//
			FileInputStream fstream = new FileInputStream(filename);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String line;
			boolean header_read = false;
			
			while ((line = br.readLine()) != null) {
				// First read file header with terms =)
				if (header_read == false) {
					String[] read_terms = line.split(";");
					
					for (int i = 0; i < read_terms.length; ++i) {
						terms.add(read_terms[i]);
					}
					
					header_read = true;
				} else {
					String key = line.substring(0, line.indexOf("|"));
					String values = line.substring(line.indexOf("|") + 1, line.length() - 1);
					
					String value[] = values.split(";");
					List<Double> val = new ArrayList<>();
					for (int i = 0; i < value.length; ++i) {
						val.add(Double.parseDouble(value[i]));
					}
					HashMap<String, List<Double>> p = new HashMap<>();
					p.put(key, val);
					features_vectors.add(p);
				}
			}
			
			in.close();
			
		} catch (Exception e) {
			System.out.println("Error: " + e.getMessage());
		}
		
	}

	@Override
	public List<HashMap<String, List<Double>>> getTrainingSet() {
		return features_vectors;
	}
	
	// -------------------------- Out of interface -------------------------------------
	
	private void create_popular_words_list() {
		for (Pair pair : labels_list) {
			most_popular_words.put(pair.second, new HashMap<String, Integer>());
		}
	}
	
	private void find_most_popular_words() {
		for (Article article : articles_list) {
			String body = parser.remove_all(article).getBody();
			String[] words = body.split(" ");
			
			if (labels_list.size() == 0) {
				System.out.println("You should put labels list first!");
			}
			String category = labels_list.get(0).first;
			
			for (int i = 0; i < words.length; ++i) {
				// Check that current word exists in words list
				
				if (most_popular_words.get(article.getValue(category)).containsKey(words[i])) {
					// OK, has this word
					int value = most_popular_words.get(article.getValue(category)).get(words[i]) + 1;
					most_popular_words.get(article.getValue(category)).put(words[i], value);
				} else {
					// Ops, has not this word
					most_popular_words.get(article.getValue(category)).put(words[i], 1);
				}
			}
		}
		
		//System.out.println(most_popular_words);
		for (int i = 0; i < labels_list.size(); ++i) {
			String cat = labels_list.get(i).second;
			//System.out.println(most_popular_words.get(cat));
			Iterator<String> it = most_popular_words.get(cat).keySet().iterator();
			System.out.println(cat);
			//for (int j = 0; j < 100; ++j) {
			//	System.out.println(it.next().toString());
			//}
		}
	}
	
	private void create_forbidden_words() {
		//
		HashMap<String, Integer> forb_prep = new HashMap<>();
		for (Pair label : labels_list) {
			ValueComparator bvc = new ValueComparator(most_popular_words.get(label.second));
			TreeMap<String, Integer> sorted = new TreeMap<String, Integer>(bvc);
			
			sorted.putAll(most_popular_words.get(label.second));
			
			//for (Map.Entry<String, Integer> entry : most_popular_words.get(label.second).entrySet()) {
			//for (Map.Entry<String, Integer> entry : sorted.entrySet()) {
			//	if (entry.getValue() >= 5) {
			//		//System.out.println(label.second + ": " + entry.getKey() + " " + entry.getValue());
			//	}
			//}
			//System.out.println("=============================================");
			
			// Create forbidden words list
			Iterator<String> forb_it = sorted.keySet().iterator();
			int limit_control = 0;
			while (forb_it.hasNext()) {
				String w = forb_it.next();
				if (forb_prep.containsKey(w)) {
					forb_prep.put(w, forb_prep.get(w) + 1);
				} else {
					forb_prep.put(w, 1);
				}
				
				limit_control++;
				if (limit_control == forb_per_cat_limit) {
					break;
				}
			}
		}
		
		// Create pure forbidden list
		Iterator<String> it = forb_prep.keySet().iterator();
		while (it.hasNext()) {
			String key = it.next();
			if (forb_prep.get(key) > 1) {
				forbidden.add(key);
			}
		}
		
		System.out.println(forbidden);
	}
	
	private void select_terms() {
		for (Pair label : labels_list) {
			ValueComparator bvc = new ValueComparator(most_popular_words.get(label.second));
			TreeMap<String, Integer> sorted = new TreeMap<String, Integer>(bvc);
			
			sorted.putAll(most_popular_words.get(label.second));
			
			// Select terms
		 	Iterator<String> it = sorted.keySet().iterator();
			List<String> probably_terms = new ArrayList<>();
			int c = 0;
			while (it.hasNext()) {
				String probably_word = it.next();
				if (!forbidden.contains(probably_word)) {
					probably_terms.add(it.next());
					c++;
				}
								
				if (c > no_terms_by_category) {
					break;
				}
			}
			
			//System.out.println(probably_terms);
			terms_by_category.put(label.second, probably_terms);
		}
		
		// Create terms
		terms.clear();
		Iterator<String> it = terms_by_category.keySet().iterator();
		while (it.hasNext()) {
			List<String> t = terms_by_category.get(it.next());
			for (int i = 0; i < t.size(); ++i) {
				terms.add(t.get(i));
			}
		}
		
		System.out.println(terms_by_category);
	}
	
	public List<Double> extraction(Article article) {
		HashMap<String, Double> vector = new HashMap<>();
		for (int i = 0; i < terms.size(); ++i) {
			vector.put(terms.get(i), 0.0);
		}
		
		String body = parser.remove_all(article).getBody();
		String[] words = body.split(" ");
		int word_counter = 0;
		
		for (int i = 0; i < words.length; ++i) {
			word_counter++;
			if (terms.contains(words[i])) {
				double c = vector.get(words[i]) + 1.0 * 0.4;//(1 * 1 - ((double)word_counter / (double)words.length) );
				vector.put(words[i], c);
			}
		}
		
		String title = parser.remove_all(article).getTitle();
		words = title.split(" ");
		
		for (int i = 0; i < words.length; ++i) {
			if (terms.contains(words[i])) {
				double c = vector.get(words[i]) + 1.0 * 0.7;//(1 * 1 - ((double)word_counter / (double)words.length) );
				vector.put(words[i], c);
			}
		}
		
		//System.out.println(vector);
		// Copy all occuraces to pure vector in correct order
		List<Double> pure_vector = new ArrayList<>();
		for (int i = 0; i < terms.size(); ++i) {
			pure_vector.add(vector.get(terms.get(i)));
		}
		//System.out.println(pure_vector);
		
		return pure_vector;
	}

	@Override
	public List<String> setKeyWords(String filename) {	/* Not in use */
		return null;
	}

}
